home *** CD-ROM | disk | FTP | other *** search
Wrap
/* MakeSpriteMovie.c This sample code shows how to create a QuickTime Sprite Track The example creates a 640 x 480 movie with one sprite track. The sprite track contains a static background picture sprite, or just a color background depending on the boolean passed into AddSpriteTrackToMovie, and three other sprites which change their properties over time. The track's media contains only one key frame sample followed by many override samples. The key frame contains all of the images used by the sprites, the override frames only contain the overrides of the locations, image indecies, and layers needed for the other sprites. Since each override frame is small the performance of the movie can be improved by flattening it. (save as self contained from Movie Player). This sample code also shows how any PICT can be recompressed with the animation compressor using a key color to obtain transparency. */ #include <Fonts.h> #include <Movies.h> #include <MediaHandlers.h> // exception handling macros #define FailIf(a, e) {if (a) { err = e; goto bail; }} #define FailOSErr(a) {if (err = a) goto bail;} #define FailMemErr(a) {a; if (err = MemError()) goto bail;} // create a sprite movie OSErr CreateSampleSpriteMovie( void ); // create a sprite track and add it to a QuickTime movie OSErr AddSpriteTrackToMovie( Movie theMovie, short trackWidth, short trackHeight, Boolean withBackgroundPicture ); // set sprite properties for non-nil parameters, overriding or adding atoms as neccessary OSErr SetSpriteData( QTAtomContainer sprite, Point *location, short *visible, short *layer, short *imageIndex ); // add a sprite to a sample OSErr AddSpriteToSample( QTAtomContainer theSample, QTAtomContainer theSprite, short spriteID ); // add a sprite key frame sample to sprite track's media OSErr AddSpriteSampleToMedia( Media theMedia, QTAtomContainer sample, TimeValue duration, Boolean isKeyFrame ); // compress a PICT with animation compressor and add image data to a sprite key sample's images container atom OSErr AddPICTImageToKeyFrameSample( QTAtomContainer keySample, short pictID, RGBColor *keyColor, short id ); // add compressed image data to a sprite key sample's images container atom OSErr AddCompressedImageToKeyFrameSample( QTAtomContainer keySample, ImageDescriptionHandle idh, long dataSize, Ptr compressedDataPtr, QTAtomID imageID ); // extract the data from a PICT which contains compressed image data OSErr ExtractCompressData( PicHandle thePict, Handle *dataOut, ImageDescriptionHandle *idh ); // helper routine for ExtractCompressData pascal void extractStdPix( PixMap *src, Rect *srcRect, MatrixRecord *matrix, short mode, RgnHandle mask, PixMap *matte, Rect *matteRect, short flags ); // recompress PICT using animation compressor with the given key color void MakePictTransparent( PicHandle pic, RGBColor *keyColor ); typedef struct { CGrafPort tempPort; Handle data; ImageDescriptionHandle idh; } extractPictRecord; #define compressDepth 16 #define compressType 'rle ' #define kSpriteTrackWidth 640 #define kSpriteTrackHeight 480 #define kIconPictID 127 #define kWorldPictID 128 #define kBackgroundPictID 158 #define kFirstSpaceShipPictID (kBackgroundPictID + 1) #define kIconImageIndex 1 #define kWorldImageIndex 2 #define kBackgroundImageIndex 3 #define kFirstSpaceShipImageIndex 4 #define kNumSpaceShipImages 24 #define kLastSpaceShipImageIndex (kFirstSpaceShipImageIndex + kNumSpaceShipImages - 1) #define kSpriteMediaTimeScale 600 #define kSpriteMediaFrameDuration 8 #define kSpriteMediaFramesPerSecond (kSpriteMediaTimeScale / kSpriteMediaFrameDuration) #define kNumOverrideSamples 199 void main(void) { InitGraf( &qd.thePort ); InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs(0L); InitCursor(); MaxApplZone(); EnterMovies(); CreateSampleSpriteMovie(); ExitMovies(); } OSErr CreateSampleSpriteMovie( void ) { OSErr err = noErr; SFReply theSFReply; FSSpec mySpec; short resRefNum = 0; short resId = 0; Movie theMovie = nil; Point where = {100,100}; SFPutFile ( where, "\pEnter movie file name:", "\pMovie File", nil, &theSFReply ); FailIf ( (! theSFReply.good ), noErr ); FSMakeFSSpec( theSFReply.vRefNum, 0, theSFReply.fName, &mySpec ); FailOSErr( CreateMovieFile ( &mySpec, 'TVOD', smCurrentScript, createMovieFileDeleteCurFile, &resRefNum, &theMovie ) ); FailOSErr( AddSpriteTrackToMovie( theMovie, kSpriteTrackWidth, kSpriteTrackHeight, true ) ); FailOSErr( AddMovieResource( theMovie, resRefNum, &resId, theSFReply.fName ) ); bail: if ( resRefNum ) CloseMovieFile ( resRefNum ); if ( theMovie ) DisposeMovie ( theMovie ); return err; } OSErr AddSpriteTrackToMovie( Movie theMovie, short trackWidth, short trackHeight, Boolean withBackgroundPicture ) { OSErr err = noErr; QTAtomContainer sample = nil; Track newTrack; Media newMedia; QTAtomContainer spriteData; RGBColor keyColor; Point location, iconLocation; short visible, layer, imageIndex, id, i, iconDelta, iconMinH, iconMaxH; keyColor.red = keyColor.green = keyColor.blue = 0xFFFF; // white // create an empty key frame sample FailOSErr( QTNewAtomContainer( &sample ) ); // add images to the key frame sample err = AddPICTImageToKeyFrameSample( sample, kIconPictID, &keyColor, 1 ); err = AddPICTImageToKeyFrameSample( sample, kWorldPictID, &keyColor, 2 ); err = AddPICTImageToKeyFrameSample( sample, kBackgroundPictID, &keyColor, 3 ); for ( i = 1; i <= kNumSpaceShipImages; i++ ) err = AddPICTImageToKeyFrameSample( sample, kFirstSpaceShipPictID + i - 1, &keyColor, i + 3 ); // create the track, add it to theMovie, and then add samples to tracks media newTrack = NewMovieTrack( theMovie, ((long)trackWidth << 16), ((long)trackHeight << 16), 0 ); newMedia = NewTrackMedia( newTrack, SpriteMediaType, kSpriteMediaTimeScale, nil, 0 ); FailOSErr( BeginMediaEdits( newMedia ) ); // add sprites to the key frame FailOSErr( QTNewAtomContainer( &spriteData ) ); if ( withBackgroundPicture ) { // background location.h = 0; location.v = 0; visible = true; layer = kBackgroundSpriteLayerNum; // this makes the sprite a background sprite imageIndex = kBackgroundImageIndex; SetSpriteData( spriteData, &location, &visible, &layer, &imageIndex ); err = AddSpriteToSample( sample, spriteData, 1 ); } // space ship sprite location.h = 0; location.v = 60; visible = true; layer = -1; imageIndex = kFirstSpaceShipImageIndex; SetSpriteData( spriteData, &location, &visible, &layer, &imageIndex ); err = AddSpriteToSample( sample, spriteData, 2 ); // world sprite location.h = trackWidth / 2 - 24; location.v = trackHeight / 2 - 24; visible = true; layer = 1; imageIndex = kWorldImageIndex; SetSpriteData( spriteData, &location, &visible, &layer, &imageIndex ); err = AddSpriteToSample( sample, spriteData, 3 ); // icon sprite iconDelta = 2; iconMinH = trackWidth / 2 - 116; iconMaxH = iconMinH + 200; iconLocation.h = iconMinH; iconLocation.v = trackHeight / 2 - 24 + 12; layer = 0; imageIndex = kIconImageIndex; SetSpriteData( spriteData, &iconLocation, &visible, &layer, &imageIndex ); err = AddSpriteToSample( sample, spriteData, 4 ); err = AddSpriteSampleToMedia( newMedia, sample, kSpriteMediaFrameDuration, true ); // now add a bunch of override frames to the media which make the space ship spin and move imageIndex = kFirstSpaceShipImageIndex; location.h = 0; location.v = 80; for ( i = 1; i < kNumOverrideSamples; i++ ) { QTRemoveChildren( sample, 0 ); QTRemoveChildren( spriteData, 0 ); // bump the image index every third frame to spin if ( (i % 3) == 0 ) { imageIndex++; if ( imageIndex > kLastSpaceShipImageIndex ) imageIndex = kFirstSpaceShipImageIndex; } // bump the location by one pixel vertically and two horizontally every frame to make it move location.h += 2; location.v++; SetSpriteData( spriteData, &location, nil, nil, &imageIndex ); err = AddSpriteToSample( sample, spriteData, 2 ); // make the icon move and change layer QTRemoveChildren( spriteData, 0 ); iconLocation.h += iconDelta; if ( iconLocation.h >= iconMaxH ) { iconLocation.h = iconMaxH; iconDelta = - iconDelta; } if ( iconLocation.h <= iconMinH ) { iconLocation.h = iconMinH; iconDelta = - iconDelta; } if ( iconDelta > 0 ) layer = 0; else layer = 3; SetSpriteData( spriteData, &iconLocation, nil, &layer, nil ); err = AddSpriteToSample( sample, spriteData, 4 ); err = AddSpriteSampleToMedia( newMedia, sample, kSpriteMediaFrameDuration, false ); } EndMediaEdits( newMedia ); InsertMediaIntoTrack( newTrack, 0, 0, GetMediaDuration( newMedia ), 0x010000 ); // add a background color to the sprite track if ( withBackgroundPicture == false ) { QTAtomContainer trackProperties; RGBColor backgroundColor; backgroundColor.red = 0x8000; backgroundColor.green = 0; backgroundColor.blue = 0xffff; QTNewAtomContainer( &trackProperties ); QTInsertChild( trackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(RGBColor), &backgroundColor, nil ); err = SetMediaPropertyAtom( newMedia, trackProperties ); QTDisposeAtomContainer( trackProperties ); } bail: if ( sample ) QTDisposeAtomContainer( sample ); if ( spriteData ) QTDisposeAtomContainer( spriteData ); return err; } OSErr SetSpriteData( QTAtomContainer sprite, Point *location, short *visible, short *layer, short *imageIndex ) { OSErr err = noErr; QTAtom propertyAtom; if ( location ) { MatrixRecord matrix; SetIdentityMatrix( &matrix ); matrix.matrix[2][0] = ((long)location->h << 16); matrix.matrix[2][1] = ((long)location->v << 16); if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyMatrix, 1, nil )) == 0 ) FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyMatrix, 1, 0, sizeof(MatrixRecord), &matrix, nil ) ) else FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(MatrixRecord), &matrix ) ); } if ( visible ) { if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyVisible, 1, nil )) == 0 ) FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyVisible, 1, 0, sizeof(short), visible, nil ) ) else FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), visible ) ); } if ( layer ) { if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyLayer, 1, nil )) == 0 ) FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyLayer, 1, 0, sizeof(short), layer, nil ) ) else FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), layer ) ); } if ( imageIndex ) { if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyImageIndex, 1, nil )) == 0 ) FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyImageIndex, 1, 0, sizeof(short), imageIndex, nil ) ) else FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), imageIndex ) ); } bail: if ( err && sprite ) QTRemoveChildren( sprite, 0 ); return err; } OSErr AddSpriteToSample( QTAtomContainer theSample, QTAtomContainer theSprite, short spriteID ) { OSErr err = noErr; QTAtom newSpriteAtom; FailIf ( QTFindChildByID( theSample, 0, kSpriteAtomType, spriteID, nil ), paramErr ); FailOSErr( QTInsertChild( theSample, 0, kSpriteAtomType, spriteID, 0, 0, nil, &newSpriteAtom ) ); // index of zero means append FailOSErr( QTInsertChildren( theSample, newSpriteAtom, theSprite ) ); bail: return err; } OSErr AddSpriteSampleToMedia( Media theMedia, QTAtomContainer sample, TimeValue duration, Boolean isKeyFrame ) { OSErr err = noErr; SampleDescriptionHandle sampleDesc = nil; FailMemErr( sampleDesc = (SampleDescriptionHandle) NewHandleClear( sizeof(SampleDescription ) ) ); FailOSErr( AddMediaSample( theMedia, (Handle) sample, 0, GetHandleSize( sample ), duration, sampleDesc, 1, isKeyFrame ? 0 : mediaSampleNotSync, nil ) ); bail: if ( sampleDesc ) DisposeHandle( (Handle) sampleDesc ); return err; } OSErr AddPICTImageToKeyFrameSample( QTAtomContainer keySample, short pictID, RGBColor *keyColor, short id ) { OSErr err = noErr; PicHandle picture; Handle compressedPicture; ImageDescriptionHandle idh; // get picture from resource picture = (PicHandle) GetPicture( pictID ); DetachResource( (Handle)picture ); // convert it to image data compressed by the animation compressor MakePictTransparent( picture, keyColor ); ExtractCompressData( picture, &compressedPicture, &idh ); // add it to the keySample HLock( compressedPicture ); AddCompressedImageToKeyFrameSample( keySample, idh, GetHandleSize( compressedPicture ), *compressedPicture, id ); bail: if ( picture ) KillPicture( picture ); if ( compressedPicture ) DisposeHandle( compressedPicture ); if ( idh ) DisposeHandle( (Handle)idh ); return err; } OSErr AddCompressedImageToKeyFrameSample( QTAtomContainer keySample, ImageDescriptionHandle idh, long dataSize, Ptr compressedDataPtr, QTAtomID imageID ) { OSErr err = noErr; Handle imageData; QTAtom defaultsAtom, imagesContainerAtom, imageAtom; // append compressed picture data to imageDescription to obtain sprite image data FailMemErr( imageData = NewHandle(0) ); FailMemErr( HandAndHand( (Handle)idh, imageData ) ); FailMemErr( PtrAndHand( compressedDataPtr, imageData, dataSize ) ); if ( (defaultsAtom = QTFindChildByIndex( keySample, 0, kSpriteSharedDataAtomType, 1, nil )) == 0 ) FailOSErr( QTInsertChild( keySample, 0, kSpriteSharedDataAtomType, 1, 0, 0, nil, &defaultsAtom ) ); if ( (imagesContainerAtom = QTFindChildByIndex( keySample, defaultsAtom, kSpriteImagesContainerAtomType, 1, nil )) == 0 ) FailOSErr( QTInsertChild( keySample, defaultsAtom, kSpriteImagesContainerAtomType, 1, 0, 0, nil, &imagesContainerAtom ) ); FailOSErr( QTInsertChild( keySample, imagesContainerAtom, kSpriteImageAtomType, imageID, 0, 0, nil, &imageAtom ) ); HLock( imageData ); FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageDataAtomType, 1, 0, GetHandleSize(imageData), *imageData, nil ) ); bail: if ( imageData ) DisposeHandle( imageData ); return err; } // PICT to compressed image conversion OSErr ExtractCompressData( PicHandle thePict, Handle *dataOut, ImageDescriptionHandle *idh ) { OSErr err = noErr; extractPictRecord state; CQDProcs procs; GrafPtr savePort; Rect bounds; if ( dataOut ) *dataOut = nil; if ( idh ) *idh = nil; GetPort( &savePort ); OpenCPort( &state.tempPort ); SetStdCProcs( &procs ); procs.newProc1 = (UniversalProcPtr)extractStdPix; state.tempPort.grafProcs = &procs; state.data = nil; state.idh = nil; SetPort( (GrafPtr)&state.tempPort ); HidePen(); bounds = (**thePict).picFrame; DrawPicture(thePict, &bounds); bail: SetPort( savePort ); CloseCPort( &state.tempPort ); *dataOut = state.data; *idh = state.idh; return err; } pascal void extractStdPix( PixMap *src, Rect *srcRect, MatrixRecord *matrix, short mode, RgnHandle mask, PixMap *matte, Rect *matteRect, short flags ) { #pragma unused(srcRect,matrix,mode,mask,matte,matteRect,flags) extractPictRecord *state; GetPort( (GrafPtr *)&state ); if ( state->idh == nil ) { ImageDescriptionHandle desc; Ptr data; long bufferSize; if ( GetCompressedPixMapInfo(src, &desc, &data, &bufferSize, nil, nil) == noErr ) { state->idh = desc; HandToHand( (Handle *)&state->idh ); PtrToHand( data, &state->data, bufferSize ); } } } void MakePictTransparent( PicHandle pic, RGBColor *keyColor ) { OSErr err; CGrafPtr savePort; GDHandle saveGD; GWorldPtr gw = nil; Rect bounds; ImageSequence seq = 0; ImageDescriptionHandle desc = nil; Ptr data = 0; long dataSize; UInt8 similarity; PicHandle newPict = nil; RGBColor saveBackColor; GetGWorld( &savePort, &saveGD ); bounds = (**(PicHandle)pic).picFrame; OffsetRect( &bounds, -bounds.left, -bounds.top ); err = NewGWorld( &gw, compressDepth, &bounds, nil, nil, useTempMem ); if (err) err = NewGWorld( &gw, compressDepth, &bounds, nil, nil, 0 ); LockPixels( gw->portPixMap ); SetGWorld( gw, nil ); GetBackColor( &saveBackColor ); RGBBackColor( keyColor ); EraseRect( &bounds ); RGBBackColor( &saveBackColor ); desc = (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) ); if ( err = MemError() ) goto bail; err = CompressSequenceBegin( &seq, gw->portPixMap, nil, &bounds, nil, compressDepth, compressType, 0, codecMaxQuality, codecMaxQuality, 0, nil, 0, desc ); if (err) goto bail; err = GetMaxCompressionSize( gw->portPixMap, &bounds, compressDepth, codecMaxQuality, compressType, 0, &dataSize ); if ( err ) goto bail; data = NewPtr( dataSize ); if ( err = MemError() ) goto bail; err = CompressSequenceFrame( seq, gw->portPixMap, &bounds, codecFlagUpdatePrevious, data, &dataSize, &similarity, nil ); if ( err ) goto bail; DrawPicture( pic, &bounds ); err = CompressSequenceFrame( seq, gw->portPixMap, &bounds, codecFlagUpdatePrevious, data, &dataSize, &similarity, nil ); if ( err ) goto bail; CDSequenceEnd( seq ); seq = 0; newPict = OpenPicture( &bounds ); err = DecompressImage( data, desc, gw->portPixMap, &bounds, &bounds, ditherCopy, nil ); ClosePicture(); if ( err ) goto bail; SetHandleSize( (Handle)pic, 0 ); HandAndHand( (Handle)newPict, (Handle)pic ); bail: CDSequenceEnd( seq ); SetGWorld( savePort, saveGD ); if ( gw ) DisposeGWorld( gw ); DisposeHandle( (Handle)desc ); if ( data ) DisposePtr( data ); KillPicture( newPict ); return; }